package com.baidu.automata.factory;

import java.io.File;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.SAXValidator;
import org.dom4j.util.XMLErrorHandler;

import com.baidu.cleaner.checker.Checker;
import com.baidu.cleaner.config.TD;
import com.baidu.cleaner.config.TR;
import com.baidu.cleaner.config.Table;
import com.baidu.cleaner.transformer.Transformer;

public class CleanerFactory {
	private static Document verifyXml(String id) throws Exception {
		XMLErrorHandler errorHandler = new XMLErrorHandler();

		// 获取基于 SAX 的解析器的实例
		SAXParserFactory factory = SAXParserFactory.newInstance();
		// 解析器在解析时验证 XML 内容。
		factory.setValidating(true);
		// 指定由此代码生成的解析器将提供对 XML 名称空间的支持。
		factory.setNamespaceAware(true);
		// 使用当前配置的工厂参数创建 SAXParser 的一个新实例。
		SAXParser parser = factory.newSAXParser();

		SAXReader xmlReader = new SAXReader();
		Document xmlDocument = (Document) xmlReader.read(new File(CleanerFactory.class.getResource("/conf/cleaner/" + id + ".xml").getFile()));

		// 设置 XMLReader 的基础实现中的特定属性。
		parser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");

		// 创建一个SAXValidator校验工具，并设置校验工具的属性
		SAXValidator validator = new SAXValidator(parser.getXMLReader());
		// 设置校验工具的错误处理器，当发生错误时，可以从处理器对象中得到错误信息。
		validator.setErrorHandler(errorHandler);
		// 校验
		validator.validate(xmlDocument);

		// 如果错误信息不为空，说明校验失败，打印错误信息
		if (errorHandler.getErrors().hasContent()) {
			throw new Exception(errorHandler.getErrors().asXML());
		}

		return xmlDocument;
	}

	private static Table makeTable(Element tableEmt) throws Exception {
		String tableName = tableEmt.attributeValue("name");
		String encoding = tableEmt.attributeValue("file_encoding");
		String empty = tableEmt.attributeValue("empty");

		Table table = new Table(tableName, encoding, Boolean.parseBoolean(empty));

		makeTR(tableEmt, table);

		return table;
	}

	private static void makeTR(Element tableEmt, Table table) throws Exception {
		Element trEmt = tableEmt.element("tr");
		String trName = trEmt.attributeValue("name");
		String fieldNum = trEmt.attributeValue("field_num");

		TR tr = new TR(trName, Integer.parseInt(fieldNum));
		table.addTR(tr);

		Element selectEmt = tableEmt.element("select");
		if (selectEmt != null) {
			String select = selectEmt.getTextTrim();
			tr.setSelectScript(select);
		}

		Element filterEmt = tableEmt.element("filter");
		if (filterEmt != null) {
			String filter = filterEmt.getTextTrim();
			tr.setFilterScript(filter);
		}
		makeTD(trEmt, tr);
	}

	@SuppressWarnings("unchecked")
	private static void makeTD(Element trEmt, TR tr) throws Exception {
		for (Iterator<Element> it = trEmt.elementIterator(); it.hasNext();) {
			Element tdEmt = it.next();
			String tdName = tdEmt.attributeValue("name");
			String index = tdEmt.attributeValue("index");
			TD td = new TD(tdName, Integer.parseInt(index));

			td.setChecker(makeChecker(tdEmt));
			td.setTransformer(makeTransformer(tdEmt));

			tr.addTD(td);
		}
	}

	@SuppressWarnings("unchecked")
	private static Checker makeChecker(Element tdEmt) throws Exception {
		Element checkerEmt = tdEmt.element("checker");
		if (checkerEmt != null) {
			// String checkerName = checkerEmt.attributeValue("name");
			String className = checkerEmt.attributeValue("class");

			Checker checker = (Checker) Class.forName(className).newInstance();

			for (Iterator<Element> cIt = checkerEmt.elementIterator(); cIt.hasNext();) {
				Element pEmt = cIt.next();
				String pName = pEmt.attributeValue("name");
				String type = pEmt.attributeValue("type");
				String value = pEmt.getTextTrim();

				setProperty(checker, pName, type, value);
			}

			return checker;
		}

		return null;
	}

	private static void setProperty(Object obj, String name, String type, String value) throws Exception {
		Method m = null;
		String functionName = "set" + StringUtils.capitalize(name);
		if (StringUtils.equalsIgnoreCase("string", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, String.class);
			m.invoke(obj, value);
		} else if (StringUtils.equalsIgnoreCase("int", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, int.class);
			m.invoke(obj, Integer.parseInt(value));
		} else if (StringUtils.equalsIgnoreCase("boolean", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, boolean.class);
			m.invoke(obj, Boolean.parseBoolean(value));
		} else if (StringUtils.equalsIgnoreCase("long", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, long.class);
			m.invoke(obj, Long.parseLong(value));
		} else if (StringUtils.equalsIgnoreCase("float", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, float.class);
			m.invoke(obj, Float.parseFloat(value));
		} else if (StringUtils.equalsIgnoreCase("double", type)) {
			m = obj.getClass().getDeclaredMethod(functionName, double.class);
			m.invoke(obj, Double.parseDouble(value));
		} else {
			throw new Exception("unknow type:" + type);
		}
	}

	@SuppressWarnings("unchecked")
	private static Transformer makeTransformer(Element tdEmt) throws Exception {
		Element transformerEmt = tdEmt.element("transformer");
		if (transformerEmt != null) {
			// String transformerName = transformerEmt.attributeValue("name");
			String className = transformerEmt.attributeValue("class");
			Transformer transformer = (Transformer) Class.forName(className).newInstance();

			for (Iterator<Element> cIt = transformerEmt.elementIterator(); cIt.hasNext();) {
				Element pEmt = cIt.next();
				String pName = pEmt.attributeValue("name");
				String type = pEmt.attributeValue("type");
				String value = pEmt.getTextTrim();

				setProperty(transformer, pName, type, value);
			}

			return transformer;
		}

		return null;
	}

	public static Table getCleaner(String id) throws Exception {
		Element tableEmt = verifyXml(id).getRootElement();

		Table table = makeTable(tableEmt);

		return table;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			getCleaner("test_validator");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
